#include <stdio.h>
#include "sys/unistd.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "Analog_IIC.h"
#include "generic_gpio.h"
#include "nvs_value.h"
#include "mqtt_cjson_handle.h"

static const char *TAG = "[IIC]";

uint8_t Write_Buffer[4]; //主控端的写入资料缓存
 
uint8_t display_address[4]={0x6e,0x6c,0x6a,0x68}; //数码管的段地址
uint8_t display_brightness[8]={0x11,0x21,0x31,0x41,0x51,0x61,0x71,0x01}; //亮度设置，分别代表从0-7级别的亮度
extern uint8_t selftest_iic_ret;

//--------------------------------------------------------------------------------------------------
//函数名称: void IIC_send_start()
//函数功能: IIC的起始位
//函数输入: 无
//函数输出: 无
//中间变量: 无
//--------------------------------------------------------------------------------------------------
void IIC_send_start() //开始位
{
    SDA_OUT;
    SDA_1; /*发送起始条件的数据信号*/
    SCL_1;
	usleep(5);
    SDA_0; /*发送起始信号*/
    usleep(5);
    SCL_0; /*此位置只需要将SCL输出为0之后等待4US即可*/
    usleep(5);
}
//--------------------------------------------------------------------------------------------------
//函数名称: void IIC_send_stop()
//函数功能: IIC的结束位
//函数输入: 无
//函数输出: 无
//中间变量: 无
//--------------------------------------------------------------------------------------------------
void IIC_send_stop() //停止位
{
    SCL_0;
    usleep(5);
    SDA_OUT;
    SDA_0; /*发送结束条件的数据信号*/
    usleep(5);
    SCL_1;
    usleep(5);
    SDA_1;
    usleep(5);
}

//--------------------------------------------------------------------------------------------------
//函数名称: bool IIC_read_ACK()
//函数功能: 读取IIC的acknowledge标志位
//函数输入: 无
//函数输出: IIC的ACK信号 返回1表示无acknowledge，0表示有acknowledge
//中间变量: 无
//--------------------------------------------------------------------------------------------------
bool IIC_read_ACK() //读取应答信号
{
    SCL_0;
    SDA_IN; /*此处为释放SDA总线，由从从机发出低电平应答*/
    usleep(5);
    SCL_1;
    usleep(5);
    if(gpio_get_level(IIC_SDA) == 1){
        ESP_LOGW(TAG, "NO ACK");
        return 1; //no ACK
    }else{
        return 0; //ACK
    }
    usleep(5);
}

//--------------------------------------------------------------------------------------------------
//函数名称: void IIC_send_ACK()
//函数功能: 主控端送出应答信号
//函数输入: 无
//函数输出: 无
//中间变量: 无
//--------------------------------------------------------------------------------------------------
void IIC_send_ACK() //输出应答信号
{
    SCL_0;
    usleep(5);
    SDA_OUT;
    SDA_0;
    usleep(5);
    SCL_1;
    usleep(5);
}
//--------------------------------------------------------------------------------------------------
//函数名称: void IIC_send_NOACK()
//函数功能: 主控端送出无应答信号
//函数输入: 无
//函数输出: 无
//中间变量: 无
//--------------------------------------------------------------------------------------------------
void IIC_send_NOACK() //输出无应答信号
{
    SCL_0;
    usleep(5);
    SDA_OUT;
    SDA_1;
    usleep(5);
    SCL_1;
    usleep(5);
}
//--------------------------------------------------------------------------------------------------
//函数名称: void IIC_send_byte(uint8_t dat)
//函数功能: 主控端写一个字节到从机
//函数输入: dat = 发送的字节
//函数输出: 无
//中间变量: i
//--------------------------------------------------------------------------------------------------
void IIC_send_byte(uint8_t dat) //写一个字节
{
    uint8_t i;
    SCL_0;
    usleep(5);
    SDA_OUT;
    for(i=0;i<8;i++)
    {
        SCL_0; /*钳住I2C总线，准备发送数据 */
        usleep(5);
        if(dat&0x80){
            SDA_1;
        }else{
            SDA_0;
        }
        usleep(5); /*如果需要在SDA,SCL,INT上串接电阻，根据电阻大小不同，电阻越大建议将该时间适当加长，100KHZ以内即可；*/
        SCL_1; /*此处由于51单片机的特性不需要做输入输出设置，
但如果是其他单片机需要先将其IO口改为输入上拉的设置，读到高之后，SCL转为输出为高。
在读写完ACK后的第一个clock下降缘从机会钳住SCL脚做资料处理，
所以将SCL脚置为输入上拉，并等待SCL被释放。*/
        usleep(5);
        dat<<=1;
    }
    usleep(5);
}
//--------------------------------------------------------------------------------------------------
//函数名称: uint8_t IIC_read_byte()
//函数功能: 主控端对从机读取一个字节
//函数输入: 无
//函数输出: 读取完成的字节
//中间变量: i, dat
//--------------------------------------------------------------------------------------------------
uint8_t IIC_read_byte() //读一个字节
{
    SCL_0;
    usleep(5);
    SDA_IN;
    usleep(5);
    uint8_t i, dat=0;
    for(i=0;i<8;i++)
    {   
        SCL_0;
        // SDA_1;
        usleep(5); /*如果需要在SDA,SCL,INT上串接电阻，根据电阻大小不同，电阻越大建议将该时间适当加长，100KHZ以内即可；*/
        dat<<=1;
        SCL_1; /*此处由于51单片机的特性不需要做输入输出设置，
但如果是其他单片机需要先将其IO口改为输入上拉的设置，读到高之后，SCL转为输出为高。
在读写完ACK后的第一个clock下降缘从机会钳住SCL脚做资料处理，
所以将SCL脚置为输入上拉，并等待SCL被释放。*/
        usleep(5);
        if(gpio_get_level(IIC_SDA) == 1){
            dat|=0x01;
        }
    }
    return dat;
}
 
void ET6226M_write_data(unsigned char add,unsigned char DATA)		//指定地址写入数据
{
	IIC_send_start();//启动信号
	IIC_send_byte(add);//发送地址
	selftest_iic_ret = IIC_read_ACK(); //应答信号
	IIC_send_byte(DATA);//写入数据
	IIC_read_ACK(); //应答信号
	IIC_send_stop(); //停止信号
}

void ET6226M_clean_all(void) //显示设备的初始化
{
	ET6226M_write_data(DIS_ADD_1,data_null);		//显示无
	ET6226M_write_data(DIS_ADD_2,data_null);
	ET6226M_write_data(DIS_ADD_3,data_null);
	ET6226M_write_data(DIS_ADD_4,data_null);
}

void ET6226M_display_all(void) //显示设备的初始化
{
	ET6226M_write_data(DIS_ADD_1,0xff);		//显示无
	ET6226M_write_data(DIS_ADD_2,0xff);
	ET6226M_write_data(DIS_ADD_3,0xff);
	ET6226M_write_data(DIS_ADD_4,0xff);
}

void ET6226M_Init(void) //显示设备的初始化
{
	ET6226M_write_data(ET6228M_COMMAND,0x09); //SYSON
	ET6226M_write_data(ET6228M_COMMAND,display_brightness[0]); //显示最大亮度
	ET6226M_display_all();
	ET6226M_clean_all();
}

void ET6226M_Change_Brightness(uint8_t num) 	//修改亮度,亮度范围0-7，0代表最低亮度，7代表最高亮度
{
	if(num < 8)
	{
        //TIM_COMMAND(.h文件定义)				0x48 //显存地址
        //display_brightness                    亮度
 
		ET6226M_write_data(ET6228M_COMMAND,display_brightness[num]); //默认为最大亮度
	}
}

//--------------------------------------------------------------------------------------------------
//函数名称: void setWrite_Buffer_4(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
//函数功能: 写入4个字节到写入缓存寄存器
//函数输入: byte1
//          byte2
//          byte3
//          byte4
//函数输出: 无
//中间变量: 无
//--------------------------------------------------------------------------------------------------
void setWrite_Buffer_4(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
{
    Write_Buffer[0] = byte1;
    Write_Buffer[1] = byte2;
    Write_Buffer[2] = byte3;
    Write_Buffer[3] = byte4;
}
//--------------------------------------------------------------------------------------------------
//函数名称: void setWrite_Buffer_3(uint8_t byte1, uint8_t byte2, uint8_t byte3)
//函数功能: 写入3个字节到写入缓存寄存器
//函数输入: byte1
//          byte2
//          byte3
//函数输出: 无
//中间变量: 无
//--------------------------------------------------------------------------------------------------
void setWrite_Buffer_3(uint8_t byte1, uint8_t byte2, uint8_t byte3)
{
    Write_Buffer[0] = byte1;
    Write_Buffer[1] = byte2;
    Write_Buffer[2] = byte3;
}

//--------------------------------------------------------------------------------------------------
//函数名称: bool writeIIC(uint8_t addrW, uint8_t *writeData, uint8_t length)
//函数功能: 主控端对从机数据写入
//函数输入: addrW = 从机地址及写入旗帜
//          *writeData = 预备写入数据的首个地址
//          length = 写入数据的长度(字节数)
//函数输出: 返回IIC通讯的acknowledge状态，若为1，则停止并返回。若为0，则完成通讯后返回
//中间变量: i, ACK
//--------------------------------------------------------------------------------------------------
bool TTY6955_write_data(uint8_t addrW, uint8_t *writeData, uint8_t length)
{
    uint8_t i;
    bool ACK;
    IIC_send_start();
    IIC_send_byte(addrW); //传送地址与写入标记
    ACK = IIC_read_ACK();
    if (ACK)
    {
        IIC_send_stop(); //地址不正确或装置未连接，送出停止信号
        return ACK;
    }
    
    for(i = 0; i<length; i++)
    {
        IIC_send_byte(writeData[i]);
        ACK = IIC_read_ACK();
        if (ACK)
        {
            IIC_send_stop(); //未接收到ACK，送出停止信号
            return ACK;
        }
    }
    IIC_send_stop(); //资料写入完成，送出停止信号
    return ACK;
}
//--------------------------------------------------------------------------------------------------
//函数名称: bool readIIC(uint8_t addrR, uint8_t *readData, uint8_t length)
//函数功能: 主控端对从机数据读取
//函数输入: addrR = 从机地址及读取旗帜
//          *readData = 预备读取后存放数据的首个地址
//          length = 读取数据的长度(字节数)
//函数输出: 返回IIC通讯的acknowledge状态，若为1，则停止并返回。若为0，则完成通讯后返回
//中间变量: i, ACK
//--------------------------------------------------------------------------------------------------
bool touchboard_read_data(uint8_t addrR, uint8_t *readData, uint8_t length)
{
    uint8_t i;
    bool ACK;
    IIC_send_start();
    IIC_send_byte(addrR); //传送地址与读取标记
    ACK = IIC_read_ACK();
    if (ACK)
    {
        IIC_send_stop(); //地址不正确或装置未连接，送出停止信号
        return ACK;
    }
    
    for(i = 0; i<length; i++)
    {
        readData[i] = IIC_read_byte();
        if(i<length-1)
            IIC_send_ACK();
        else
            IIC_send_NOACK(); //读取最后一笔资料，送出No ACK
    }
    IIC_send_stop(); //资料读取完成，送出停止信号
    return ACK;
}

void TTY6955_write_setting(void)
{
    bool ACK = 1;
    uint8_t step = 0;
    uint8_t error_count = 0;

    while(1){
        switch(step){
            case 0:
                setWrite_Buffer_4(0xB5, 0x2B, 0x70, 0x00);
                // setWrite_Buffer_4(0xB5, 0x2B, 0x33, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 4); //MCU Setting
                break;
            case 1:
                setWrite_Buffer_3(0xC0, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP0 Threshold
                break;
            case 2:
                setWrite_Buffer_3(0xC1, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP1 Threshold
                break;
            case 3:
                setWrite_Buffer_3(0xC2, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP2 Threshold
                break;
            case 4:
                setWrite_Buffer_3(0xC3, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP3 Threshold
                break;
            case 5:
                setWrite_Buffer_3(0xC4, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP4 Threshold
                break;
            case 6:
                setWrite_Buffer_3(0xC5, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP5 Threshold
                break;
            case 7:
                setWrite_Buffer_3(0xC6, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP6 Threshold
                break;
            case 8:
                setWrite_Buffer_3(0xC7, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP7 Threshold
                break;
            case 9:
                setWrite_Buffer_3(0xC8, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP8 Threshold
                break;
            case 10:
                setWrite_Buffer_3(0xC9, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP9 Threshold
                break;
            case 11:
                setWrite_Buffer_3(0xCA, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP10 Threshold
                break;
            case 12:
                setWrite_Buffer_3(0xCB, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP11 Threshold
                break;
            case 13:
                setWrite_Buffer_3(0xCC, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP12 Threshold
                break;
            case 14:
                setWrite_Buffer_3(0xCD, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP12 Threshold
                break;
            case 15:
                setWrite_Buffer_3(0xCE, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP12 Threshold
                break;
            case 16:
                setWrite_Buffer_3(0xCF, 0x08, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //TP12 Threshold
                break;
            case 17:
                setWrite_Buffer_3(0xD0, 0x02, 0x00);
                ACK = TTY6955_write_data(TTY6955_addr_W, Write_Buffer, 3); //Sleep Threshold
                break;
            default:
                break;
        }
        if(ACK == 0){
            step++;
            if(step == 18){
                // selftest_iic_ret = 1;
                nvs_record_error(0, 4);
                ESP_LOGI(TAG, "TTY6955 init finish.");
                break;
            }
        }else{
            step = 0;
            error_count++;
            if(error_count > 3){
                // selftest_iic_ret = 2;
                nvs_record_error(1, 4);
                cJSON_creat_error(401);
                break;
            }
            vTaskDelay(500 / portTICK_PERIOD_MS);
            ESP_LOGE(TAG, "TTY6955 write setting error");
        }
    }
}

